package org.elephantt.webby;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * A responder that picks a contained responder based on matching rules.
 */
public class MultiResponder implements Responder {
  private List<ResponderEntry> responders = new LinkedList<ResponderEntry>();

  private static class ResponderEntry {
    private final RequestMatcher requestMatcher;
    private final Responder responder;

    private ResponderEntry(RequestMatcher requestMatcher, Responder responder) {
      this.requestMatcher = requestMatcher;
      this.responder = responder;
    }

    public RequestMatcher getRequestMatcher() {
      return requestMatcher;
    }

    public Responder getResponder() {
      return responder;
    }
  }

  public interface RequestMatcher {
    boolean isMatch(HttpServletRequest request);
  }

  public void addResponder(RequestMatcher requestMatcher, Responder responder) {
    responders.add(new ResponderEntry(requestMatcher, responder));
  }

  /**
   * TODO: allow isMatch containing '*'
   * TODO: allow specification of multiple values (e.g., application/xml and text/xml)
   */
  public static class AcceptHeaderMatcher implements RequestMatcher {
    private final String acceptHeader;

    public AcceptHeaderMatcher(String acceptHeader) {
      this.acceptHeader = acceptHeader;
    }

    public boolean isMatch(HttpServletRequest request) {
      return acceptHeader != null && acceptHeader.equals(request.getHeader("Accept"));
    }
  }

  public static class QueryParamMatcher implements RequestMatcher {
    private final String queryParamKey;
    private final String queryParamValue;

    public QueryParamMatcher(String queryParamKey, String queryParamValue) {
      this.queryParamKey = queryParamKey;
      this.queryParamValue = queryParamValue;
    }

    public boolean isMatch(HttpServletRequest request) {
      return queryParamValue.equals(request.getParameter(queryParamKey));
    }
  }

  protected Responder matchResponderForRequest(HttpServletRequest request) {
    Responder responder = null;
    Iterator<ResponderEntry> i = responders.iterator();
    while (responder == null && i.hasNext()) {
      ResponderEntry entry = i.next();
      if (entry.getRequestMatcher().isMatch(request)) {
        responder = entry.getResponder();
      }
    }

    return responder;
  }

  /**
   * Find the first responder that isMatch and use it.
   * @throws WebbyException if no responder isMatch
   */
  public void respond(HttpServletRequest request, HttpServletResponse response, Object controllerReturnValue) throws IOException {
    Responder responder = matchResponderForRequest(request);
    if (responder != null) {
      responder.respond(request, response, controllerReturnValue);
    } else {
      throw new WebbyException("No responder for request " + request);
    }
  }
}
